;;;This is the formula p(a|b) = p(a , b) / (b)
;;; @a: (([key:value] [value])*)
;;; @b: (([key:value] [value])*)
(defun b.findAnswer (a b table)
  (let ((temp (/ (f2 (append a b) table)
		 (f2 b table))))
    (if (> temp 1) 1
    temp)))

;;;This is the formula p(a|b) and reduces b to just dependent
;;; @a: ('[key:value] [value])
;;; @b: (('[key:value] [value])*) 
(defun f1 (a b table &optional (corrThreshold .333) (valueNotFound nil))
  (setq a (list a))
  ;(format t "@f1 a: ~a b: ~a~%" a b)
  (if (p a b table) (p a b table);can find case in table
    (let ((dependents))
      (loop for b_n in b do
            (if (and (p a (list b_n) table);make sure that doesn't return nil
                     (> (abs (/ (- (p a nil table)
                                   (p a (list b_n) table))
                                (p a nil table)))
                                   
                        corrThreshold));making sure that a is dependent on b
                (setq dependents (cons b_n
                                       dependents))))
      ;(format t " f1.dependents: ~a" dependents)
      (if (zerop (- (list-length b)
                    (list-length dependents)));If b == dependents
          valueNotFound
        (f1 (car a) dependents table corrThreshold valueNotFound)))))

;;;This is the formula p(a_1 a_2 ... a_n) = p(a_1 | a_2 ... a_n) * ...*p(a_[n-1] | a_n)*P(a_n)
;;; @a: ((['key:value] [value])*)
(defun f2 (a table)
  ;(format t "@f2 a: ~a ~%" a)
  (if (not a) (return-from f2 1));if empty list return 1
  (let ((all (p a nil table)))
    (if all
	all;We don't need to break it up
	(let ((left))
	  (do ((right a (cdr right)))
	      ((not right)
	       (* (f2 (list (car a)) table)
		  (f2 (cdr a) table)));No possible break up
	    (let* ((restOf 
		    (append left (cdr right)))
		   (temp (f1 
		    (car right)
		    restOf
		    table)))
	      ;(format t " f2: @restOf: ~a @temp: ~a ~%"
		      ;restOf
		     ; temp)
	       (if temp
		   (return-from f2 (* temp
				      (f2 restOf table))) ;if possible break up
		   (setq left
			 (cons (car right)
			       left)))))))));Add right to left and continue loop 



;;;Probability that value|given, returns nil if no answer could be found
;;;@returns: float in range (0, 1)
;;; @value: (('[key:value] [value])*)
;;; @given: (('[key:value] [value])*)
;;; @table: list of hashtables
(defun p (value given table)
  ;(format t "@p:: value: ~a given: ~a" value given)
  (let ((totalFound 0);tables that have given
        (totalWith 0));tables with outcome and given
    (do ((outcome table (cdr outcome)))
        ((not outcome))
      (if (findC given (car outcome) value)
        (progn (setq totalFound (+ totalFound 1));if table has been found with givens
          (if (findC (append value given) (car outcome))
              (setq totalWith (+ totalWith 1))))));if table has givens and outcome
    (if (or (zerop totalFound)
	    (zerop totalWith))
	nil
	(/ totalWith
	   (+ totalFound 0))))); return (totalWith + 1)/(totalFound + 2)  and this way p is bounded by (0,1) not [0,1] though this creates error

;;;Can find in hash table all the elements
;;; @elements: (('[key:value] [value])*) check to make sure in hash and have same values
;;; @outcome: [value] the hash table
(defun findC 
       (elements outcome &optional other) ;find if table has all elements and their values
  ;(format t "@findc: elements: ~a other:~a~%" elements other)
  (do ((e elements (rest e)))
      ((not e))
    (let ((ele (car e)))
      (if (not (equal (gethash (cadar ele) 
                                 outcome)
                        (cadr ele)))
            
	  (return-from findC nil));Short circuit if ele not found or value doesn't match
      )    
    )
  (do ((e other (rest e)));Just going to make sure that there is at least the elements
      ((not e))
    (let ((ele (car e)))
      (if (equal (gethash (cadar ele) 
                                 outcome
				 'notfound)
		 'notfound)
	  (progn 
	    ;(format t "@findc.ele: ~a @find.(cadaar ele): ~a ~%" ele (cadaar ele))
	    (return-from findC nil)));Short circuit if ele of rest is not found
      )    
    )
  T)       
